#include <fs.h>
#include <unistd.h>
typedef size_t (*ReadFn) (void *buf, size_t offset, size_t len);
typedef size_t (*WriteFn) (const void *buf, size_t offset, size_t len);

typedef struct {
  char *name;
  size_t size;
  size_t disk_offset;
  ReadFn read;
  WriteFn write;
  size_t open_offset;  // 当前文件操作位置
} Finfo;

enum {FD_STDIN, FD_STDOUT, FD_STDERR, FD_EVENT, FD_DISPINFO, FD_FB};

size_t invalid_read(void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}

size_t invalid_write(const void *buf, size_t offset, size_t len) {
  panic("should not reach here");
  return 0;
}
extern size_t serial_write(const void *buf, size_t offset, size_t len);
extern size_t events_read(void *buf, size_t offset, size_t len);
extern size_t dispinfo_read(void *buf, size_t offset, size_t len);
extern size_t fb_write(const void *buf, size_t offset, size_t len);
/* This is the information about all files in disk. */
static Finfo file_table[] __attribute__((used)) = {
  [FD_STDIN]  = {"stdin", 0, 0, invalid_read, invalid_write},
  [FD_STDOUT] = {"stdout", 0, 0, invalid_read, serial_write},
  [FD_STDERR] = {"stderr", 0, 0, invalid_read, serial_write},
  [FD_EVENT]  = {"/dev/events", 0, 0, events_read, invalid_write},
  [FD_DISPINFO] = {"/proc/dispinfo", 0, 0, dispinfo_read, invalid_write},
  [FD_FB]     = {"/dev/fb", 0, 0, invalid_read, fb_write},
#include "files.h"
};

void init_fs() {
  // TODO: initialize the size of /dev/fb
  AM_GPU_CONFIG_T info = io_read(AM_GPU_CONFIG);
  file_table[FD_FB].size = info.width * info.height * 4;
 
}


extern size_t ramdisk_read(void *buf, size_t offset, size_t len);
extern size_t ramdisk_write(const void *buf, size_t offset, size_t len);

int fs_open(const char *pathname, int flags, int mode){ 
  int size = (sizeof(file_table) / sizeof(file_table[0]));
  for(int i = 3; i < size; i++){
    // printf("The file name is %s\n",file_table[i].name);
    if(strcmp(file_table[i].name, pathname) == 0 ){
      return i;
    }
  }
  assert(0);
}
size_t fs_read(int fd, void *buf, size_t len){
  Finfo file = file_table[fd];
  if(file.open_offset + len > file.size){
    len = file.size - file.open_offset;
  }
  if(file.read != NULL){
    len = file_table[fd].read(buf, file.disk_offset + file.open_offset, len);
    file_table[fd].open_offset += len;
    return len;
  }
  ramdisk_read(buf, file.disk_offset + file.open_offset, len);
  // Log("The start is %d",file.disk_offset + file.open_offset);
  file_table[fd].open_offset += len;
  
  return len;
}
size_t fs_write(int fd, const void *buf, size_t len){
  // if(fd == 1 || fd == 2){
  //   for(int i = 0; i < len; i++){
  //     putch(*(char *)(buf + i));
  //   }
  //   return len;
  // }

  Finfo file = file_table[fd];
  if(file.open_offset + len > file.size){
    len = file.size - file.open_offset;
  }
  if(file.write != NULL){
    file_table[fd].write(buf, file.disk_offset + file.open_offset, len);
    return len;
  }
  ramdisk_write(buf, file.disk_offset + file.open_offset, len);
  file_table[fd].open_offset += len;
  return len;
}
size_t fs_lseek(int fd, size_t offset, int whence){
  switch (whence)
  {
    case SEEK_SET: file_table[fd].open_offset = offset > file_table[fd].size ? file_table[fd].size : offset; break;
    case SEEK_CUR: file_table[fd].open_offset = (offset + file_table[fd].open_offset) > file_table[fd].size ? file_table[fd].size : offset; break;
    case SEEK_END: file_table[fd].open_offset = file_table[fd].size + offset; break;
  }
  return file_table[fd].open_offset;
}
int fs_close(int fd){
  file_table[fd].open_offset = 0;
  return 0;
}